from PyQt4.QtCore import QObject, SIGNAL, QSize,  Qt
from PyQt4.QtGui import *
from time import sleep
from threading import Thread
from hashlib import md5
import os, pickle
#import recursos

#____________ FUNÇOES _______________
def CalcHash( NomeArquivo, Argumentos = {'porcentagem':0, 'abortar':'nao',
                                          'hash_calculado':''}, Funcao = False ):
    chash = md5()
    Argumentos['abortar']='nao'
    try:
        arquivo = open(NomeArquivo, 'rb')
        
        fim = arquivo.seek( 0, 2 )
        arquivo.seek( 0 )
        cont = 0
        Argumentos['porcentagem'] = 0
    
        partes = iter( lambda : arquivo.read( 1000000 ), "" )
    
        while Argumentos['abortar'] == 'nao':
            while cont != fim:
                for bloco in partes:
                    chash.update( bloco )
                    cont = arquivo.tell()
                    Argumentos['porcentagem'] = int((cont / fim) * 100)
                    break
                break
            else:
                Argumentos['abortar'] = 'proximo'
                
        if Argumentos['abortar'] == 'sim' and Funcao == False :
            Argumentos['hash_calculado'] = 'Abortado'
        elif Argumentos['abortar'] == 'sim' and Funcao == True:
            return 'Abortado'
        elif Funcao == True:
            return chash.hexdigest()
        else:
            Argumentos['hash_calculado'] = chash.hexdigest()
            Argumentos['abortar'] = 'nao'
    
        arquivo.close()
    except IOError as erro:
        print(erro)
    
def CargArquivos(ListaEntrada,  ListaSaida,  QTabela):
    
    maisArquivos = QFileDialog.getOpenFileNames()
    
    for arquivo in maisArquivos:
        if arquivo in ListaEntrada:
            continue
        nomeArq = str(os.path.basename(arquivo))
        tamArq = str(round(os.stat(arquivo).st_size / 1000))
        ListaEntrada.append(arquivo)
        ListaSaida.append([nomeArq,  tamArq, ''])

    PreenQTable(QTabela,  ListaSaida)

def PreenQTable(QTabela,  Lista,  
                HeaderLabels = ['Nome', 'Tamanho (KB)', 'MD5 Hash'], ColunaTamanho = 1, ColunasHash = [2],
                BlockItens = []):
    QTabela.clear()
    QTabela.setRowCount(len(Lista))
    QTabela.setColumnCount(len(HeaderLabels))
    QTabela.setHorizontalHeaderLabels(HeaderLabels)
    for coluna in ColunasHash:
        QTabela.setColumnWidth(coluna, 200)

    linha = 0
    for campo in Lista: 
        nitem=0
        for item in campo:
            cont_item = QTableWidgetItem(str(item))
            if ColunaTamanho != None:
                if item == campo[ColunaTamanho]:
                    cont_item.setTextAlignment(Qt.AlignVCenter|Qt.AlignRight)
            if BlockItens != []:
                for bloq in BlockItens:
                    if nitem == bloq:
                        cont_item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
                
            QTabela.setItem(linha, nitem, cont_item)
            nitem += 1
        linha += 1
    
def SalvaCSV(mestre, Lista, Topo = ['Nome','Tamanho (KB)', 'MD5 Hash']):
    
    local = QFileDialog.getSaveFileName(mestre, 'Salvar lista de hash', 
    '', 'Comma-separated values (*.csv)')
    local = os.path.abspath(local)
    
    try:
        salvar = open(local, 'w', encoding = 'utf8')
    
        for item in Topo:
            if item == Topo[-1]:
                salvar.write(str(item+'\n'))
            else:
                salvar.write(str(item+';'))
        
        for campo in Lista:
            for item in campo:
                if item == campo[-1]:
                    salvar.write(str(item+'\n'))
                else:
                    salvar.write(str(item+';'))
        
        salvar.close()
    except IOError:
        pass

def Refazer(linha, coluna, QTabela, Lista, *argsTabela, **kwTabela):
    itemAtual=QTabela.item(QTabela.currentRow(), QTabela.currentColumn())
    if itemAtual != None:
        itemLista = Lista[linha]
        itemLista = itemLista[coluna]
        itemAtual = itemAtual.text()
        if itemAtual != itemLista:
            PreenQTable(QTabela, Lista, *argsTabela, **kwTabela)
    
def actComparar(Lista1,  Lista2, ListaSaida, ColunaDeHash = 2,  ColunaNomes = 0, ListaIndices=[]):
    listaHash1 = []
    listaNomes1 = []
    haDiferenca = False

    for linha in Lista1:
        listaHash1.append(linha[ColunaDeHash])
        listaNomes1.append(linha[ColunaNomes])

    for linha2 in Lista2:
        if linha2[ColunaDeHash] not in listaHash1:
            haDiferenca = True
            if linha2[ColunaNomes] in listaNomes1:
                nLinha = listaNomes1.index(linha2[ColunaNomes])
                hashLista1 = listaHash1[nLinha]
            else:
                hashLista1 = 'Não identificado'
            linhaDiferenca = linha2[:]
            linhaDiferenca.append(hashLista1)
            if linhaDiferenca not in ListaSaida:
                ListaSaida.append(linhaDiferenca)
            ListaIndices.append(Lista2.index(linha2))

    if haDiferenca:
        return True
    else: return False
    
def CargaCSV(mestre):
    lista = []
    nomeArquivo = QFileDialog.getOpenFileName(mestre, 'Carregar arquivo csv',
                                                '','Comma-separated values (*.csv)')
    nomeArquivo=os.path.abspath(nomeArquivo)
    
    try:
        with open(nomeArquivo, 'r', encoding = 'utf8') as arquivo:
            topo = arquivo.readline().split(';')
            ultItem = topo[-1]
            topo[-1] = ultItem[:-1]
            
            for linha in arquivo.readlines():
                itens = linha.split(';')
                ultItem = itens[-1]
                if ultItem == 'Abortado\n':
                    ultItem = 'Não calculado\n'
                itens[-1] = ultItem[:-1]
                
                if itens not in lista:
                    lista.append(itens)
                
        return topo, lista
    except IOError:
        pass
    
def Dividir(Origem, Destino, Tamanho, DicionControle = {'abortar':'nao', 'porcentParte':0, 'porcentTotal':0}, 
            DicionResultados = {'NomeOriginal':'', 'NomeDestino':'', 'HashTotal':'', 'TotalPartes':0, 
            'NumDigitos':0, 'TamanhoTotal':0,'ListaPartes': [] }):
    arqOrigem = open(Origem, 'rb')
    fimOrigem = arqOrigem.seek(0, 2)
    arqOrigem.seek(0)
    
    tamPartes = Tamanho * 1000000
    tamanOrigem = os.stat(Origem).st_size
    numDigitos = len(str(int(tamanOrigem / tamPartes) + 1))
    
    DicionResultados['TamanhoTotal'] = tamanOrigem
    DicionResultados['NumDigitos'] = numDigitos
    
    posOrigAtual = 0
    parteAtual = 0
    hashTotal = md5()
    
    miniPartes = iter( lambda: arqOrigem.read(1000000), "" )
    
    listaPartes = DicionResultados['ListaPartes']
    
    while posOrigAtual != fimOrigem and DicionControle['abortar'] != 'sim':
        parteAtual += 1
        hashParte = md5()
        
        extensao = '.'+str(parteAtual).rjust(numDigitos, '0')
        nomeAtual = Destino+extensao
        arqAtual = open(nomeAtual, 'wb')
        
        while os.stat(nomeAtual).st_size < tamPartes and DicionControle['abortar'] != 'sim' :
            DicionControle['porcentParte'] = int((os.stat(nomeAtual).st_size / tamPartes)*100)
            
            for mini in miniPartes:
                arqAtual.write( mini )
                hashParte.update( mini )
                hashTotal.update( mini )
                break
                
            posOrigAtual = arqOrigem.tell()
            
            if posOrigAtual == fimOrigem:
                DicionControle['porcentTotal'] = 100
                break
        listaPartes.append([os.path.basename(nomeAtual), hashParte.hexdigest()])
        DicionControle['porcentTotal'] = int((posOrigAtual / fimOrigem)*100)
        
        arqAtual.close()
    arqOrigem.close()
    
    DicionResultados['HashTotal'] = hashTotal.hexdigest()
    DicionResultados['TotalPartes'] = parteAtual
    
def Emendar(Origem, Destino, DicionCHS, DicionControle = {'abortar':'nao', 'porcentParte':0, 
            'porcentTotal':0, 'conferir':True, 'gravar':True, 'questao':[0,'', ''], 'pausa':False}, 
            ListaErro = [], qPorcParte = QObject, qPorcTotal = QObject, qSigFechar = QObject, qQuestao = QObject, 
            avisarTodos = True):

    
    listQuestao = DicionControle['questao']
    caminDest = os.path.abspath(str(Destino+'/'+DicionCHS['NomeOriginal']))
    
    pathPartes = os.path.dirname(Origem)
    listaPartes = DicionCHS['ListaPartes']
    escrevendo = False
    
    if DicionControle['gravar']: 
        if os.path.isfile(caminDest):
            # QUESTAO 1
            DicionControle['pausa'] = True
            listQuestao[1] = DicionCHS['NomeOriginal']
            qQuestao.emit(SIGNAL('questao(int)'), 1)
            
            while DicionControle['pausa']: sleep(0.5)
            
            if listQuestao[0] == 'ok':
                arqTotal = open(caminDest, 'wb')
                escrevendo = True
                listQuestao[0] = 0
        else:
            arqTotal = open(caminDest, 'wb')
            escrevendo = True
    
    md5ResulTotal = md5()

    numParteAtual = 0
    
    for nomeHashArq in listaPartes:
        if DicionControle['abortar'] == 'sim':
            break
        
        md5ResulParte = md5()
        while DicionControle['abortar'] != 'sim':
            pathAtual = os.path.abspath(str(pathPartes+'/'+nomeHashArq[0]))
            if os.path.isfile(pathAtual):
                arqAtual = open(pathAtual, 'rb')
                break
            else:
                DicionControle['pausa'] = True
                listQuestao[1] = nomeHashArq[0]
                listQuestao[2] = pathPartes
                qQuestao.emit(SIGNAL('questao(int)'), 2)
                #QUESTAO 2
                while DicionControle['pausa']: sleep(0.5)
                
                
                if listQuestao[0] == 'ok':
                    pathPartes = str(listQuestao[2])
                
        if DicionControle['abortar'] == 'sim':
            break
        
        miniPartes = iter( lambda: arqAtual.read(1000000), "" )
        
        fimParte = arqAtual.seek(0,2)
        arqAtual.seek(0)
        posiParte = 0
        
        while posiParte != fimParte and DicionControle['abortar'] != 'sim':
            for mini in miniPartes:
                if DicionControle['gravar']:
                    arqTotal.write(mini)
                
                posiParte = arqAtual.tell()
                DicionControle['porcentParte'] = int(posiParte/fimParte*100)
                qPorcParte.emit(SIGNAL('valueChanged(int)'), DicionControle['porcentParte'])
                
                if DicionControle['conferir']:
                    md5ResulParte.update(mini)
                    md5ResulTotal.update(mini)
                break
        
        arqAtual.close()
        
        numParteAtual += 1
        DicionControle['porcentTotal'] = int(numParteAtual/len(listaPartes)*100)
        qPorcTotal.emit(SIGNAL('valueChanged(int)'), DicionControle['porcentTotal'])
        
        
        if DicionControle['conferir'] and DicionControle['abortar'] != 'sim':
            if md5ResulParte.hexdigest() != nomeHashArq[1]:
                nomeHashArqAtual = nomeHashArq[:]
                nomeHashArqAtual.append(md5ResulParte.hexdigest())
                ListaErro.append(nomeHashArqAtual[:])
                if avisarTodos:
                    # QUESTAO 3
                    DicionControle['pausa'] = True
                    listQuestao[1] = nomeHashArq[0]
                    qQuestao.emit(SIGNAL('questao(int)'), 3)
                    while DicionControle['pausa']: sleep(0.5)
                    
                    if listQuestao[2] == 0:
                        DicionControle['abortar'] = 'sim'
                    elif listQuestao[2] == 1:
                        pass
                    elif listQuestao[2] == 2:
                        avisarTodos = False
                        pass
    
    if DicionControle['gravar'] and escrevendo:
        arqTotal.close()
    
    if DicionControle['conferir']:
        if md5ResulTotal.hexdigest() != DicionCHS['HashTotal']:
            if os.path.isfile(caminDest) and escrevendo:
                erroTotal = [str(DicionCHS['NomeOriginal']), str(DicionCHS['HashTotal']), md5ResulTotal.hexdigest()]
                ListaErro.append(erroTotal[:])
                # QUESTAO 4
                DicionControle['pausa'] = True
                listQuestao[1] = DicionCHS['NomeOriginal']
                qQuestao.emit(SIGNAL('questao(int)'), 4)
                while DicionControle['pausa']: sleep(0.5)

                if listQuestao[2] == 0:
                    os.remove(caminDest)
    
    qSigFechar.emit(SIGNAL('fechar'))
    

def AbJanela(classeJanela, *args, **kwargs):
    janela = classeJanela( *args, **kwargs)
    janela.exec()
    
def NovoTamanho(QJanela, Altura, Largura):
    novoTamanho = QSize()
    atualTamanho = QJanela.frameSize()
    
    if atualTamanho.width() < Largura:
        novoTamanho.setWidth(Largura)
    else:
        novoTamanho.setWidth(atualTamanho.width())
    if atualTamanho.height() < Altura:
        novoTamanho.setHeight(Altura)
    else:
        novoTamanho.setHeight(atualTamanho.height())
    
    QJanela.resize(novoTamanho)
    
def Transferencia(ListaArquivosIN, Destino, qSigLabel = QObject, qSigFim = QObject, 
                  qSigPorcent = QObject, ListaArquivosOUT = [], ListaOutTabela=[], ListaErro=[], 
                  dicControle = {'abortar':False, 'paraLista':False, 'substituir':False}, 
                  DicionParaCHS={}):
                      
    if dicControle['paraLista']:
        md5Total = md5()
    
    dicControle['abortar']=False
    
    DicionParaCHS['ListaPartes']=[]
    
    numArqTotal = len(ListaArquivosIN)
    numAtual = 0
    
    for arquivo in ListaArquivosIN:
        nomeBase = os.path.basename(arquivo)
        md5Atual = md5()
        numAtual += 1
        qSigLabel.emit(SIGNAL('valueChanged(int,int)'), numAtual, numArqTotal)
        qSigPorcent.emit(SIGNAL('valueChanged(int)'), 0)
        
        try:
            arqOrigem = open(arquivo, 'rb')
            miniPartes = iter( lambda : arqOrigem.read( 1000000 ), "" )
            fimArq = arqOrigem.seek(0, 2)
            arqOrigem.seek(0)
            tamArq = str(round(os.stat(arquivo).st_size / 1000))
        except IOError:
            ListaErro.append([nomeBase, tamArq, 'Erro na leitura do arquivo'])
            continue
            
        if dicControle['abortar']:
            ListaErro.append([nomeBase, tamArq, 'Abortado'])
            continue
        
        if not dicControle['paraLista']:
            camDestino = os.path.abspath(str(Destino+'/'+nomeBase))
            if os.path.isfile(camDestino) and not dicControle['substituir']:
                ListaErro.append([nomeBase, tamArq, 'Arquivo já existente'])
                continue
            else:
                try:
                    arqDestino = open(camDestino, 'wb')
                except IOError:
                    ListaErro.append([nomeBase, tamArq, 'Erro de escrita no destino'])
                    continue
        
        for mini in miniPartes:
            if dicControle['abortar']: 
                ListaErro.append([nomeBase, tamArq, 'Abortado'])
                break
            
            posArq = arqOrigem.tell()
            md5Atual.update(mini)
            
            if dicControle['paraLista']:
                md5Total.update(mini)
            else:
                arqDestino.write(mini)
            
            qSigPorcent.emit(SIGNAL('valueChanged(int)'), int(posArq / fimArq * 100))
            if fimArq == posArq:
                break
            
        ListaOutTabela.append([nomeBase, tamArq, md5Atual.hexdigest()])
        if not dicControle['paraLista']:
            ListaArquivosOUT.append(camDestino)
        else:
            DicionParaCHS['ListaPartes'].append([nomeBase, md5Atual.hexdigest()])
            
        arqOrigem.close()
        
    if dicControle['paraLista'] and ListaErro == [] and not dicControle['abortar']:
        tamTotal = 0
        for arquivo in ListaOutTabela:
            tamTotal += int(arquivo[1])
        DicionParaCHS['HashTotal'] = md5Total.hexdigest()
        DicionParaCHS['TamanhoTotal'] = tamTotal
        DicionParaCHS['TotalPartes'] = len(DicionParaCHS['ListaPartes'])
        
    if not dicControle['paraLista']:
        try:
            arqDestino.close()
        except UnboundLocalError:
            pass
        
    qSigFim.emit(SIGNAL('fechar'))
    
def listarArquivos(ListaArquivos = [], ListaParaTabela=[], numCampos=2, parent = None, 
                   AdicionarNovos = True, listaNovos = []):
    if AdicionarNovos:
        listaNovos = QFileDialog.getOpenFileNames(parent, 'Selecionar arquivos')
    
    for arquivo in listaNovos:
        if not arquivo in ListaArquivos:
            ListaArquivos.append(arquivo)
            tamArq = str(round(os.stat(arquivo).st_size / 1000))
            nomeBase = os.path.basename(arquivo)
            if numCampos == 1:
                ListaParaTabela.append(nomeBase)
            elif numCampos == 2:
                ListaParaTabela.append([nomeBase, tamArq])
            elif numCampos > 2:
                maisCampos = numCampos - 2
                linhaMais = []
                linhaMais.append(nomeBase)
                linhaMais.append(tamArq)
                for campo in range(maisCampos):
                    linhaMais.append('')
                ListaParaTabela.append(linhaMais[:])
            else:
                raise ValueError('Valor numCampos menor que 1')
#================================= JANELA ========================================

class ChecSplit(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self)
        self.setWindowTitle('ChecSplit')
        self.setWindowIcon(QIcon('icon.png'))
        
        mainQuadro = QFrame()
#========= INTEGRIDADE ==========
        groupIntegridade = QGroupBox()
        groupIntegridade.setTitle('INTEGRIDADE')

        btHash = QPushButton('&HASH')
        btHash.setFixedWidth(100)
        lblHash = QLabel('Calcula o hash md5 de arquivos')
        btComparar = QPushButton('&COMPARAR')
        btComparar.setFixedWidth(100)
        lblComparar = QLabel('Compara arquivos com lista .csv\nou com outros arquivos')

        layIntegridade = QGridLayout()
        layIntegridade.addWidget(btHash,0,0)
        layIntegridade.addWidget(lblHash,0,1)
        layIntegridade.addWidget(btComparar,1,0)
        layIntegridade.addWidget(lblComparar,1,1)
        groupIntegridade.setLayout(layIntegridade)

#========= DIVISAO ==============
        groupDivisao = QGroupBox('DIVISÃO')
        btDivisao = QPushButton('&DIVIDIR')
        btDivisao.setFixedWidth(100)
        lblDivisao = QLabel('Divide um arquivo em várias partes.')
        btEmendar = QPushButton('&EMENDAR')
        lblEmendar = QLabel('Emenda as partes do arquivo.')
        btConferir = QPushButton('C&ONFERIR')
        lblConferir = QLabel('Confere integridade de arquivos divididos.')
        btLista = QPushButton('&GERAR CHS')
        lblLista = QLabel('Gera uma lista chs a partir de arquivos\nselecionados')

        layDivisao = QGridLayout()
        layDivisao.addWidget(btDivisao,0,0)
        layDivisao.addWidget(lblDivisao,0,1)
        layDivisao.addWidget(btEmendar,1,0)
        layDivisao.addWidget(lblEmendar,1,1)
        layDivisao.addWidget(btConferir,2,0)
        layDivisao.addWidget(lblConferir,2,1)
        layDivisao.addWidget(btLista, 3, 0)
        layDivisao.addWidget(lblLista, 3, 1)

        groupDivisao.setLayout(layDivisao)

#========= COPIA ================
        groupCopia = QGroupBox('CÓPIA')
        btCopiar = QPushButton('CO&PIAR')
        btCopiar.setFixedWidth(100)
        lblCopiar = QLabel('Cópia de arquivos com checagem de\nintegridade.')

        layCopia = QGridLayout()
        layCopia.addWidget(btCopiar,0,0)
        layCopia.addWidget(lblCopiar,0,1)

        groupCopia.setLayout(layCopia)

#================================
        layout=QVBoxLayout()
        layout.addWidget(groupIntegridade)
        layout.addWidget(groupDivisao)
        layout.addWidget(groupCopia)

        mainQuadro.setLayout(layout)
        self.setCentralWidget(mainQuadro)
#========= AÇOES ================
        self.connect(btHash, SIGNAL('clicked()'), lambda: AbJanela(
            JanHash, parent = self))
        self.connect(btComparar, SIGNAL('clicked()'), lambda: AbJanela(
            JanComparar, parent = self))
        self.connect(btDivisao, SIGNAL('clicked()'), lambda: AbJanela(
            JanDividir, parent = self))
        self.connect(btEmendar, SIGNAL('clicked()'), lambda: AbJanela(
            JanEmendar, parent = self))
        self.connect(btConferir, SIGNAL('clicked()'), lambda: AbJanela(
            JanConferir, parent = self))
        self.connect(btCopiar, SIGNAL('clicked()'), lambda: AbJanela(
            JanCopiar, parent = self))
        self.connect(btLista, SIGNAL('clicked()'), lambda: AbJanela(
                                                                    JanGerarCHS, parent = self))
            
#========= SOBRE ================
        menubar = self.menuBar()
        #menubar.setLayoutDirection(1)
        sobre = QAction('&Sobre', self)
        
        menubar.addAction(sobre)
        
        self.connect(sobre, SIGNAL('triggered()'),  lambda: Sobre())
        
        def Sobre():
            texto = '''
ChecSplit 1.1 (201104)


Desenvolvido por:
Dyego M.B. (dyegomb@gmail.com)

ChecSplit está sob os termos de GNU GPL v3.
Copyright (c) 2010 Dyego M. B.'''
            QMessageBox.about(self, 'ChecSplit', texto)

#++++++++++ HASH =================
class JanHash(QDialog):
    def __init__(self,parent=None):
        super(JanHash,self).__init__(parent)
        self.setWindowTitle('ChecSplit - Hash')
        self.setModal(True)
        tamanho = QSize()
        tamanho.setHeight(250)
        tamanho.setWidth(530)
        self.resize(tamanho)
        self.setWindowFlags(Qt.Window)
        
        #____ Variaveis ____
        arquivosLista = []
        listaArqTam = []
        
        btCarregar  = QPushButton(' Carregar arquivos ')
        btCalcular = QPushButton('Calcular')
        btSalvarCsv = QPushButton(' Salvar lista ')
        tableArquivos = QTableWidget()
        tableArquivos.setRowCount(0)
        tableArquivos.setColumnCount(3)
        tableArquivos.setHorizontalHeaderLabels(['Nome', 'Tamanho (KB)', 'MD5 Hash'])
        tableArquivos.setSortingEnabled(False)
        tableArquivos.setAlternatingRowColors(True)
        tableArquivos.setSelectionMode(QAbstractItemView.SingleSelection)

        layBotoes = QVBoxLayout()
        layBotoes.addWidget(btCalcular)
        layBotoes.addWidget(btSalvarCsv)
        layBotoes.addStretch()
        layBotsTab = QGridLayout()
        layBotsTab.addWidget(tableArquivos, 0, 0)
        layBotsTab.columnStretch(1)
        layBotsTab.addLayout(layBotoes, 0, 1)
        layHash = QVBoxLayout()
        layHash.addWidget(btCarregar, 1, Qt.AlignHCenter)
        layHash.addLayout(layBotsTab)
        
        self.setLayout(layHash)

        self.connect(btSalvarCsv, SIGNAL('clicked()'),  lambda:
                     SalvaCSV(self, listaArqTam))
        self.connect(btCarregar, SIGNAL('clicked()'), lambda: CargArquivos(arquivosLista,  listaArqTam, 
                                                                           tableArquivos))
        self.connect(btCalcular, SIGNAL('clicked()'), lambda: actBtCalcular())
        self.connect(tableArquivos, SIGNAL('cellChanged(int,int)'), 
        lambda a, b: Refazer(a, b, tableArquivos, listaArqTam))
        
        def actBtCalcular():
            AbJanela(JanHashCalc, arquivosLista, listaArqTam, parent = self)
            PreenQTable(tableArquivos,  listaArqTam)

class JanHashCalc(QDialog):
    def __init__(self, ListaEntrada,  ListaSaida, parent=None, titulo = 'Calculando'):
        super(JanHashCalc,self).__init__(parent)
        self.setWindowTitle(titulo)
        self.setModal(True)
        self.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        
        lblPogresso = QLabel('Calculando 0 de 0')
        progBarra = QProgressBar()
        progBarra.setValue(0)
        btAbortar = QPushButton('Abortar')
        
        layout = QVBoxLayout()
        layout.addWidget(lblPogresso, 1, Qt.AlignHCenter)
        layout.addWidget(progBarra)
        layout.addWidget(btAbortar, 1, Qt.AlignHCenter)
        self.setLayout(layout)
        
        sinal = QObject()
        porcentagem = QObject()
        
        self.connect(self, SIGNAL('rejected()'), lambda: actAbortar())
        self.connect(sinal, SIGNAL('fechar()'), lambda: actAbortar())
        self.connect(porcentagem, SIGNAL('valueChanged(int)'), progBarra.setValue)
        self.connect(btAbortar, SIGNAL('clicked()'), lambda: actAbortar())
        
        #____ Variaveis ____
        argumentos = {'porcentagem':0, 'abortar':'nao',
                                          'hash_calculado':''}
                                          
        def actAbortar():
            argumentos['abortar'] = 'sim'

            self.close()
            
        
        def Progresso():
            total = len(ListaEntrada)
            for arquivo in ListaEntrada:
                nAtual = ListaEntrada.index(arquivo)
                if argumentos['abortar'] == 'sim':
                    break
                else:
                    lblPogresso.setText('Calculando '+str(nAtual+1)+' de '+str(total))
                threadHash = Thread(target = CalcHash,  args = [arquivo,  argumentos])
                threadHash.start()
                while threadHash.isAlive():
                    if argumentos['abortar'] == 'sim':
                        break
                    else:
                        porcentagem.emit(SIGNAL('valueChanged(int)'),  argumentos['porcentagem'])
                if argumentos['abortar'] == 'sim':
                    break
                else:
                    porcentagem.emit(SIGNAL('valueChanged(int)'),  100)
                campo = ListaSaida[nAtual]
                campo[2] = argumentos['hash_calculado']
                
            for arquivo in ListaSaida:
                if arquivo[2] == '':
                    arquivo[2] = 'Abortado'
                
            if argumentos['abortar'] == 'sim':
                pass
            else:
                sinal.emit(SIGNAL('fechar()'))
        
        threadProg = Thread(target = Progresso)
        threadProg.start()
#++++++++++ COMPARAR =================
class JanComparar(QDialog):
    def __init__(self,parent=None):
        super(JanComparar,self).__init__(parent)
        self.setWindowTitle('ChecSplit - Comparar')
        self.setModal(True)
        self.setWindowFlags(Qt.Window)
        
        groupOriginais = QGroupBox('Arquivos originais')
        btCargOrig = QPushButton('Carregar arquivos')
        btCargLista = QPushButton('Carregar lista')
        tableOrig = QTableWidget()
        tableOrig.setAlternatingRowColors(True)
        
        groupCompara = QGroupBox('Arquivos a serem comparados')
        btCargCompara = QPushButton(' Carregar arquivos ')
        btCargLista2 = QPushButton('Carregar lista')
        tableCompara = QTableWidget()
        tableCompara.setAlternatingRowColors(True)
        
        btComparar = QPushButton(' Comparar ')
        btComparar.setDefault(True)
        
        groupDiferenca = QGroupBox('Arquivos diferentes')
        tableDiferenca = QTableWidget()
        tableDiferenca.setAlternatingRowColors(True)
        btSalvLista = QPushButton(' Salvar lista ')
        
        #___ LAYOUT ___
        layBtOrig = QHBoxLayout()
        layBtOrig.addWidget(btCargOrig)
        layBtOrig.addWidget(btCargLista)
        layOriginais = QVBoxLayout()
        layOriginais.addLayout(layBtOrig)
        layOriginais.addWidget(tableOrig)
        groupOriginais.setLayout(layOriginais)

        layGroups = QVBoxLayout()
        layBtComp = QHBoxLayout()
        layBtComp.addWidget(btCargCompara)
        layBtComp.addWidget(btCargLista2)
        layGroups.addLayout(layBtComp)
        layGroups.addWidget(tableCompara)
        groupCompara.setLayout(layGroups)
        
        layAmbas = QHBoxLayout()
        layAmbas.addWidget(groupOriginais)
        layAmbas.addWidget(groupCompara)
        
        layGroupDiferenca = QVBoxLayout()
        layGroupDiferenca.addWidget(tableDiferenca)
        layGroupDiferenca.addWidget(btSalvLista, 1, Qt.AlignHCenter)
        groupDiferenca.setLayout(layGroupDiferenca)
        
        layout = QVBoxLayout()
        layout.addLayout(layAmbas)
        layout.addWidget(btComparar, 1, Qt.AlignHCenter)
        
        self.setLayout(layout)
        
        #___ VARIAVEIS ___
        listaCaminho1 = []
        listaCaminho2 = []
        listaHash1 = []
        listaHash2 = []
        listaComparacao = []
        
        #___ AÇÕES ___
        self.connect(btCargOrig, SIGNAL('clicked()'),  lambda: CargArquivos(listaCaminho1, listaHash1, 
                                                                            tableOrig))
        self.connect(btCargCompara, SIGNAL('clicked()'),  lambda: CargArquivos(listaCaminho2, listaHash2, 
                                                                            tableCompara))
        self.connect(btComparar, SIGNAL('clicked()'), lambda: lancaComparacao())
        self.connect(tableOrig, SIGNAL('cellChanged(int,int)'), 
        lambda a, b: Refazer(a, b, tableOrig, listaHash1))
        self.connect(tableCompara, SIGNAL('cellChanged(int,int)'), 
        lambda a, b: Refazer(a, b, tableCompara, listaHash2))
        self.connect(tableDiferenca, SIGNAL('cellChanged(int,int)'), 
        lambda a, b: Refazer(a, b, tableDiferenca, listaComparacao, 
                             HeaderLabels = ['Nome', 'Tamanho (KB)', 'Hash comparado', 'Hash original'], 
                             ColunasHash = [2, 3]))
        self.connect(btSalvLista,  SIGNAL('clicked()'), lambda: SalvaCSV(self, listaComparacao, 
                                                                          Topo = ['Nome', 'Tamanho (KB)', 'Hash diferente', 
                                                                                  'Hash comparado']))
        self.connect(btCargLista,  SIGNAL("clicked()"), lambda: CarregarCSV(1))
        self.connect(btCargLista2,  SIGNAL("clicked()"), lambda: CarregarCSV(2))
        
        #___ FUNÇÕES ___
        def lancaComparacao():
            calcular1 = False
            calcular2 = False
            
            for linha in listaHash1:
                if linha[2] in ['', 'Abortado']:
                    calcular1 = True
                    
            if calcular1:
                AbJanela(JanHashCalc, listaCaminho1, listaHash1, 
                                         parent = self, titulo = 'Lista 1')
                PreenQTable(tableOrig, listaHash1)
            
            for linha in listaHash2:
                if linha[2] in ['', 'Abortado']:
                    calcular2 = True
            if calcular2:
                AbJanela(JanHashCalc, listaCaminho2, listaHash2, 
                                         parent = self, titulo = 'Lista 2')
                PreenQTable(tableCompara, listaHash2)
            
            haDiferenca = actComparar(listaHash1,  listaHash2, listaComparacao)
            if haDiferenca:
                layout.addWidget(groupDiferenca)
                PreenQTable(tableDiferenca, listaComparacao, HeaderLabels = ['Nome', 'Tamanho (KB)', 'Hash comparado', 
                                                             'Hash original'], ColunasHash = [2, 3])
                NovoTamanho(self, 500, 530)
            else:
                QMessageBox.information(self, 'Comparação', 'Diferenças não foram encontradas.')
                
        def CarregarCSV(opcao):
            try:
                topo, lista = CargaCSV(self)
                if len(topo) != 3 :
                    QMessageBox.critical(self, 'Comparação', 'Arquivo não apropriado para esta tarefa.')
                else:
                    for item in lista:
                        if opcao == 1:
                            if item not in listaHash1:
                                listaHash1.append(item)
                        elif opcao == 2:
                            if item not in listaHash2:
                                listaHash2.append(item)
                    
                    if opcao == 1:
                        PreenQTable(tableOrig, listaHash1)
                    elif opcao == 2:
                        PreenQTable(tableCompara, listaHash2)
            except TypeError:
                pass
            except ValueError:
                QMessageBox.critical(self, 'Carregando .csv', 'Arquivo não apropriado para esta tarefa.')
#++++++++++ DIVIDIR =================
class JanActPartes(QDialog):
    def __init__(self, Dicionario, Origem, Destino, *args, tamanho = 0, parent=None, titulo = 'Partes',
                 ListaErro = []):
        super(JanActPartes,self).__init__(parent)
        self.setWindowTitle(titulo)
        self.setModal(True)
        self.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        
        lblTrabalhando = QLabel('Trabalhando')
        
        lblParte = QLabel('Parte:')
        pgbParte = QProgressBar()
        pgbParte.setValue(0)
        
        lblTotal = QLabel('Total:')
        pgbTotal = QProgressBar()
        pgbTotal.setValue(0)
        
        btAbortar = QPushButton(' Abortar ')
        
        #___ LAYOUT ___
        layProgresso = QGridLayout()
        layProgresso.addWidget(lblParte, 0, 0)
        layProgresso.addWidget(pgbParte, 0, 1)
        layProgresso.addWidget(lblTotal, 1, 0)
        layProgresso.addWidget(pgbTotal, 1, 1)
        
        layout = QVBoxLayout()
        layout.addWidget(lblTrabalhando, 1, Qt.AlignHCenter)
        layout.addLayout(layProgresso)
        layout.addWidget(btAbortar, 1, Qt.AlignHCenter)
        
        self.setLayout(layout)
        
        porcParte = QObject()
        porcTotal = QObject()
        sinalFechar = QObject()
        QQuestao = QObject()
        
        self.connect(porcParte, SIGNAL('valueChanged(int)'), pgbParte.setValue)
        self.connect(porcTotal, SIGNAL('valueChanged(int)'), pgbTotal.setValue)
        self.connect(sinalFechar, SIGNAL('fechar'), self.close)
        self.connect(self, SIGNAL('rejected()'), lambda: quandoFechar())
        
        self.connect(btAbortar, SIGNAL('clicked()'), lambda: actAbortar())
        
        self.connect(QQuestao, SIGNAL('questao(int)'), lambda e: Questoes(e))
        
        #___ VARIÁVEIS ___
#        porcentagem = [0, 0]
        dicionControle = {'abortar':'nao', 'porcentParte':0, 'porcentTotal':0, 
        'conferir':True, 'gravar':True, 'questao':[0,'', ''], 'pausa':False}
        
        #___ AÇÕES ___
        
        
        if 'dividir' in args:
            threadProcesso = Thread(target = Dividir, args=[Origem, Destino, tamanho], 
                                   kwargs = {'DicionControle':dicionControle,
                                   'DicionResultados':Dicionario})
            threadProcesso.start()
            
            def Porcent():
                while threadProcesso.isAlive():
                    porcParte.emit(SIGNAL('valueChanged(int)'), dicionControle['porcentParte'])
                    porcTotal.emit(SIGNAL('valueChanged(int)'), dicionControle['porcentTotal'])
                else:
                    sinalFechar.emit(SIGNAL('fechar'))
            
            threadPorcent = Thread(target = Porcent)
            threadPorcent.start()
            
        elif 'emendar' in args:
            if 'conferir' in args:
                threadProcesso = Thread(target = Emendar, args=[Origem, Destino, Dicionario], 
                                       kwargs={'DicionControle':dicionControle, 'ListaErro':ListaErro, 
                                               'qPorcParte':porcParte, 'qPorcTotal':porcTotal, 
                                               'qSigFechar':sinalFechar, 'qQuestao':QQuestao})
                threadProcesso.start()
                #threadPorcent.start()
                
            else:
                dicionControle['conferir']=False
                
                threadProcesso = Thread(target = Emendar, args=[Origem, Destino, Dicionario], 
                                       kwargs={'DicionControle':dicionControle, 'ListaErro':ListaErro, 
                                               'qPorcParte':porcParte, 'qPorcTotal':porcTotal, 
                                               'qSigFechar':sinalFechar, 'qQuestao':QQuestao})
                threadProcesso.start()
            
        elif 'conferir' in args:
            dicionControle['gravar']=False
            
            threadProcesso = Thread(target = Emendar, args=[Origem, Destino, Dicionario], 
                                       kwargs={'DicionControle':dicionControle, 'ListaErro':ListaErro, 
                                               'qPorcParte':porcParte, 'qPorcTotal':porcTotal, 
                                               'qSigFechar':sinalFechar, 'qQuestao':QQuestao, 'avisarTodos' : False})
            threadProcesso.start()
        #___ FUNÇÕES ___
        def actAbortar():
            dicionControle['abortar'] = 'sim'
            
        def quandoFechar():
            if dicionControle['abortar'] == 'sim':
                QMessageBox.critical(parent, 'ChecSplit', 'O processo foi abortado.')
            elif ListaErro != []:
                QMessageBox.critical(parent, 'ChecSplit', 'O processo apresentou erro.')
            elif not dicionControle['conferir']:
                QMessageBox.information(parent, 'ChecSplit', 'Concluído.\nObs.:sem checagem integridade!')
            else:
                QMessageBox.information(parent, 'ChecSplit', 'Concluído com êxito!')
                
            #___ QUESTÕES ___
        def Questoes(intQuestao):
            listQuestao = dicionControle['questao']
            if intQuestao == 1:
                questao = QMessageBox.warning( self, 'Sobrescrever', 'Arquivo '+listQuestao[1]+' já existe.\nDeseja sobrescrevê-lo?', 
                                              'Sim', 'Não')
                if questao == 0:
                    listQuestao[0] = 'ok'
                    dicionControle['pausa'] = False
                if questao == 1:
                    dicionControle['abortar'] = 'sim'
                    dicionControle['pausa'] = False
            elif intQuestao == 2:
                questao = QMessageBox.warning(self, str('Arquivo '+listQuestao[1]), 
                                    str('O arquivo '+listQuestao[1]+' não foi encontrado.\nDeseja localizá-lo?'), 
                                    'Sim', 'Não')
                if questao == 0:
                    listQuestao[2] = QFileDialog.getExistingDirectory(self, 'Selecionar o local do arquivo '+listQuestao[1], 
                                                                      directory = listQuestao[2])
                    listQuestao[0] = 'ok'
                    dicionControle['pausa'] = False
                else:
                    dicionControle['abortar'] = 'sim'
                    dicionControle['pausa'] = False
                    
            elif intQuestao == 3:
                listQuestao[2] = QMessageBox.critical(parent, 'Erro', 
                                                               'O arquivo '+listQuestao[1]+' está corrompido.\nAbortar processo?', 
                                                               'Abortar', 'Ignorar', ' Ignorar todos ')
                dicionControle['pausa'] = False
            elif intQuestao == 4:
                listQuestao[2] = QMessageBox.critical(parent, 'Erro', 
                'O arquivo resultante apresentou diferença do original.\nDeseja excluir o arquivo '+listQuestao[1]+'?', 
                'Sim', 'Não')
                dicionControle['pausa'] = False
                
class JanDividir(QDialog):
    def __init__(self,parent=None):
        super(JanDividir,self).__init__(parent)
        self.setWindowTitle('ChecSplit - Dividir')
        self.setModal(True)
        self.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        
        btArqOrigem = QPushButton(' Arquivo de &origem ')
        inputOrigem = QLineEdit()
        btArqDestino = QPushButton(' &Nome de destino ')
        inputDestino = QLineEdit()
        
        lblTamanho = QLabel('&Especifique o tamanho das partes em MB:')
        spinTamanho = QSpinBox()
        lblTamanho.setBuddy(spinTamanho)
        spinTamanho.setMaximum(9999999)
        
        btDividir = QPushButton(' &Dividir ')
        btDividir.setToolTip('Será gerado um arquivo com extensão .chs\
 que deve \nser mantido juntamente com os demais arquivos gerados.')
        btDividir.setDefault(True)
        
        
        #___ LAYOUT ___
        layLinhas = QGridLayout()
        layLinhas.addWidget(btArqOrigem, 0, 0)
        layLinhas.addWidget(inputOrigem, 0, 1)
        layLinhas.addWidget(btArqDestino, 1, 0)
        layLinhas.addWidget(inputDestino, 1, 1)
        
        layTamanho = QHBoxLayout()
        layTamanho.addWidget(lblTamanho)
        layTamanho.addWidget(spinTamanho)
        layTamanho.addStretch()
        
        layout = QVBoxLayout()
        layout.addLayout(layLinhas)
        layout.addLayout(layTamanho)
        layout.addWidget(btDividir, 1, Qt.AlignHCenter)
        
        self.setLayout(layout)
        
        #___ AÇÕES ___
        self.connect(btArqOrigem, SIGNAL('clicked()'), lambda: SelArqGrande())
        self.connect(btArqDestino, SIGNAL('clicked()'), lambda: SelNomePartes())
        self.connect(btDividir, SIGNAL('clicked()'), lambda: lancaDividir())
        
        #___ VARIAVEIS ___
        DicResultPartes = {'NomeOriginal':'', 'NomeDestino':'', 'TamanhoTotal':0, 
        'HashTotal':'', 'TotalPartes':0, 'NumDigitos':0, 'ListaPartes': []}
        
        #___ FUNÇÕES ___
        def SelArqGrande():
            arquivo = QFileDialog.getOpenFileName(self, 'Selecionar arquivo a ser dividido.')
            inputOrigem.setText(arquivo)
        
        def SelNomePartes():
            origem = inputOrigem.text()
            if os.path.isfile(origem):
                arquivo = QFileDialog.getSaveFileName(self, 'Selecionar nome de destino', 
                                                      directory = os.path.dirname(origem))
            else:
                arquivo = QFileDialog.getSaveFileName(self, 'Selecionar nome de destino')
                
            inputDestino.setText(arquivo)
        
        def lancaDividir():
            origem = inputOrigem.text()
            destino = inputDestino.text()
            tamanho = spinTamanho.value()
            
            if not os.path.isfile(origem):
                questao = QMessageBox.warning(self, 'Selecionar arquivo', 
                'Para continuar é necessário selecionar o arquivo a ser dividido.\nDeseja continuar?', 
                'Sim', 'Não')
                if questao == 0:
                    SelArqGrande()
                    lancaDividir()
            elif not os.path.isabs(destino) or destino =='':
                questao = QMessageBox.warning(self, 'Selecionar destino', 
                'Para continuar é necessário selecionar o nome e local de destino das partes.\n\Deseja continuar?', 
                'Sim', 'Não')
                if questao == 0:
                    SelNomePartes()
                    lancaDividir()
            elif tamanho < 1:
                QMessageBox.critical(self, 'Selecionar destino', 
                'Para continuar é necessário especificar o tamanho das partes.', 
                QMessageBox.Ok|QMessageBox.Default)
                
                spinTamanho.setFocus()
                spinTamanho.selectAll()
            else:
                DicResultPartes['NomeOriginal'] = os.path.basename(origem)
                DicResultPartes['NomeDestino'] = os.path.basename(destino)
                dividindo = JanActPartes(DicResultPartes, origem, destino, 
                'dividir', tamanho = tamanho, parent=self, titulo = 'Dividindo')
                dividindo.exec()
                
                with open(str(destino+'.chs'), 'wb') as arquivoCHS:
                    pickle.dump(DicResultPartes, arquivoCHS)

#++++++++++ EMENDAR =================
class JanEmendar(QDialog):
    def __init__(self,parent=None):
        super(JanEmendar,self).__init__(parent)
        self.setWindowTitle('ChecSplit - Emendar')
        self.setModal(True)
        self.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        
        btCargCHS = QPushButton(' Arquivo .chs ')
        inputCHS = QLineEdit()
        btDestino = QPushButton(' Destino ')
        inputDestino = QLineEdit()
        
        chkChecar = QCheckBox()
        chkChecar.setText('Checar integridade durante processo')
        chkChecar.setChecked(True)
        
        btEmendar = QPushButton(' Emendar ')
        btEmendar.setDefault(True)
        
        groupCorrompidos = QGroupBox('Arquivos que apresentaram erro')
        tableCorrompidos = QTableWidget()
        btSalvarCSV = QPushButton(' Salvar lista ')

        #___ LAYOUT ___
        layLinhas = QGridLayout()
        layLinhas.addWidget(btCargCHS, 0, 0)
        layLinhas.addWidget(inputCHS, 0, 1)
        layLinhas.addWidget(btDestino, 1, 0)
        layLinhas.addWidget(inputDestino, 1, 1)
        
        layGroupCorromp = QVBoxLayout()
        layGroupCorromp.addWidget(tableCorrompidos)
        layGroupCorromp.addWidget(btSalvarCSV, 1, Qt.AlignHCenter)
        groupCorrompidos.setLayout(layGroupCorromp)
        
        layout = QVBoxLayout()
        layout.addLayout(layLinhas)
        layout.addWidget(chkChecar)
        layout.addWidget(btEmendar, 1, Qt.AlignHCenter)
        
        self.setLayout(layout)
        
        #___ AÇÕES ___
        self.connect(btCargCHS,  SIGNAL('clicked()'), lambda: selArqCHS())
        self.connect(btDestino,  SIGNAL('clicked()'), lambda: selDestino())
        self.connect(btEmendar, SIGNAL('clicked()'), lambda: lancaEmendar())
        self.connect(btSalvarCSV, SIGNAL('clicked()'), lambda: salvarCSV())
        
        #___ VARIÁVEIS ___
        listaErro = []
        
        #___ FUNÇÕES ___
        def selArqCHS():
            arqCHS = QFileDialog.getOpenFileName(self, 'Selecionar arquivo chs', '', 
            'Arquivo ChecSplit (*.chs)')
            inputCHS.setText(arqCHS)
            
        def selDestino():
            dirOrigem = os.path.dirname(inputCHS.text())
            
            if os.path.isdir(dirOrigem):
                dirDestino = QFileDialog.getExistingDirectory(self, 'Selecionar destino', directory = dirOrigem)
            else:
                dirDestino = QFileDialog.getExistingDirectory(self, 'Selecionar destino')
            inputDestino.setText(dirDestino)
            
        def lancaEmendar():
            origem = inputCHS.text()
            destino = inputDestino.text()
            
            listaErro = []
            
            if not os.path.isfile(origem):
                questao =QMessageBox.warning(self, 'Emendar', 'É necessário selecionar o arquivo chs.\n\Deseja selecionar agora?', 'Sim', 'Não')
                if questao == 0:
                    selArqCHS()
            elif not os.path.isdir(destino):
                questao = QMessageBox.warning(self, 'Emendar', 'É necessário selecionar um diretório\
 de destino.\nDeseja selecionar agora?', 'Sim', 'Não')
                if questao == 0:
                    selDestino()
            elif chkChecar.isChecked():
                try:
                    with open(origem, 'rb') as arqCHS: DicResultPartes = pickle.load(arqCHS)
                    emendando = JanActPartes(DicResultPartes, origem, destino, 
                    'emendar', 'conferir', parent=self, titulo = 'Emendando', ListaErro = listaErro)
                    emendando.exec()
                except EOFError:
                    QMessageBox.critical(self, 'Emendar', 'Arquivo chs inválido.')
                
            elif not chkChecar.isChecked():
                try:
                    with open(origem, 'rb') as arqCHS: DicResultPartes = pickle.load(arqCHS)
                    emendando = JanActPartes(DicResultPartes, origem, destino, 
                    'emendar', parent=self, titulo = 'Emendando')
                    emendando.exec()
                except EOFError:
                    QMessageBox.critical(self, 'Emendar', 'Arquivo chs inválido.')
                
                
            if listaErro != []:
                layout.addWidget(groupCorrompidos)
                PreenQTable(tableCorrompidos,  listaErro, 
                            HeaderLabels = ['Nome', 'MD5 Hash original', 'MD5 Hash apresentado'], ColunaTamanho = None, 
                            ColunasHash = [1, 2])
                novoTamanho = QSize()
                atualTamanho = self.frameSize()
                
                if atualTamanho.width() < 590:
                    novoTamanho.setWidth(590)
                else:
                    novoTamanho.setWidth(atualTamanho.width())
                if atualTamanho.height() < 370:
                    novoTamanho.setHeight(370)
                else:
                    novoTamanho.setHeight(atualTamanho.height())
                
                self.resize(novoTamanho)
               
        def salvarCSV(): 
            SalvaCSV(self, listaErro, Topo = ['Nome', 'MD5 Hash original', 'MD5 Hash apresentado'])

#++++++++++ LISTAR =================
class JanGerarCHS(QDialog):
    def __init__(self,parent=None):
        super(JanGerarCHS,self).__init__(parent)
        self.setWindowTitle('ChecSplit - Gerar CHS')
        self.setModal(True)
        self.setWindowFlags(Qt.Window)
        
        btCarregar = QPushButton(' Carregar arquivos ')
        tableArquivos = QTableWidget()
        tableArquivos.setAlternatingRowColors(True)
        btDestinoCHS = QPushButton(' Salvar chs ')
        btDestinoCHS.setToolTip('A ordem apresentada na lista acima será mantida no arquivo chs')
        
        #___ LAYOUT ___
        layout = QVBoxLayout()
        layout.addWidget(btCarregar, 1, Qt.AlignHCenter)
        layout.addWidget(tableArquivos)
        layout.addWidget(btDestinoCHS, 1, Qt.AlignHCenter)
        self.setLayout(layout)
        
        #___ AÇÕES ___
        self.connect(btCarregar,  SIGNAL('clicked()'), lambda: cargArquivos())
        self.connect(btDestinoCHS, SIGNAL('clicked()'), lambda: actGerarCHS())
        
        #___ VARIAVEIS ___
        listaArqIN = []
        dicioCHS = {}
        dicioControle = {'abortar':False, 'paraLista':True, 'substituir':False}
        listaTabela=[]
        
        #___ FUNÇÕES ___
        def cargArquivos():
            listaCargAdd = QFileDialog.getOpenFileNames(self)
            listarArquivos(ListaArquivos = listaArqIN, AdicionarNovos = False, 
                           ListaParaTabela=listaTabela, numCampos=2, listaNovos = listaCargAdd)
            PreenQTable(tableArquivos,  listaTabela,  HeaderLabels = ['Nome', 'Tamanho (KB)'], ColunaTamanho = 1, 
            BlockItens=[0, 1])
            
        def actGerarCHS():
            try:
                nomeArquivo = list(os.path.splitext(listaArqIN[0]))
                nomeArquivo[0] = str(nomeArquivo[0]+".chs")
            except Exception:
                nomeArquivo=["", ""]
            destinoCHS = QFileDialog.getSaveFileName(self, 'Salvar arquivo chs', nomeArquivo[0],
            'Arquivo ChecSplit (*.chs)')
            dicioCHS['NomeDestino']=os.path.basename(destinoCHS)
            dicioCHS['NomeOriginal'], extCHS=os.path.splitext(dicioCHS['NomeDestino'])
            dicioCHS['NumDigitos']=0
            
            AbJanela(janTransf, parent = self, LabelTexto='Calculando', ListaArqIN=listaArqIN, 
                     DicControle=dicioControle, DicParaCHS=dicioCHS)
            
            if not dicioControle['abortar'] and os.path.isabs(destinoCHS):
                with open(destinoCHS, 'wb') as arqCHS:
                    pickle.dump(dicioCHS, arqCHS)


#++++++++++ CONFERIR =================
class JanConferir(QDialog):
    def __init__(self,parent=None):
        super(JanConferir,self).__init__(parent)
        self.setWindowTitle('ChecSplit - Conferir')
        self.setModal(True)
        self.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        
        btCargCHS = QPushButton(' Arquivo .chs ')
        inputCargCHS = QLineEdit()
        
        btConferir = QPushButton(' Conferir ')
        
        groupCorromp = QGroupBox('Arquivos corrompidos')
        tableCorromp = QTableWidget()
        tableCorromp.setAlternatingRowColors(True)
        btSalvarCSV = QPushButton(' Salvar lista ')
        btSalvarCSV.setToolTip('''Salva a lista de arquivos corrompidos
e seus hashes em um arquivo .csv''')
        
        #___ LAYOUT ___
        layLinha = QHBoxLayout()
        layLinha.addWidget(btCargCHS)
        layLinha.addWidget(inputCargCHS)
        
        layCorrompidos = QVBoxLayout()
        layCorrompidos.addWidget(tableCorromp)
        layCorrompidos.addWidget(btSalvarCSV, 1, Qt.AlignHCenter)
        groupCorromp.setLayout(layCorrompidos)
        
        layout = QVBoxLayout()
        layout.addLayout(layLinha)
        layout.addWidget(btConferir, 1, Qt.AlignHCenter)
        
        self.setLayout(layout)
        
        #___ AÇÕES ___
        
        self.connect(btCargCHS,  SIGNAL('clicked()'), lambda: selArqCHS())
        self.connect(btConferir, SIGNAL('clicked()'), lambda: lancaConferir())
        self.connect(btSalvarCSV, SIGNAL('clicked()'), lambda: salvarCSV())
        
        #___ VARIÁVEIS ___
        listaErro = []
        
        #___ FUNÇÕES ___
        def selArqCHS():
            arqCHS = QFileDialog.getOpenFileName(self, 'Selecionar arquivo chs', '', 
            'Arquivo ChecSplit (*.chs)')
            inputCargCHS.setText(arqCHS)
        
        def salvarCSV():
            SalvaCSV(self, listaErro, Topo = ['Nome', 'MD5 Hash original', 'MD5 Hash apresentado'])
        
        def lancaConferir():
            origem = inputCargCHS.text()
            destino = ''
            
            if not os.path.isfile(origem):
                questao =QMessageBox.warning(self, 'Emendar', '''É necessário selecionar o arquivo chs.
Deseja selecionar agora?''', 'Sim', 'Não')
                if questao == 0:
                    selArqCHS()
            
            try:
                with open(origem, 'rb') as arqCHS: dicioCHS = pickle.load(arqCHS)
                janela = JanActPartes(dicioCHS, origem, destino, 'conferir', parent=self, titulo = 'Conferindo',
                     ListaErro = listaErro)
                janela.exec()
            except EOFError:
                QMessageBox.critical(self, 'Emendar', 'Arquivo chs inválido.')
            except IOError:
                pass
                
            if listaErro != []:
                layout.addWidget(groupCorromp)
                PreenQTable(tableCorromp,  listaErro, 
                            HeaderLabels = ['Nome', 'MD5 Hash original', 'MD5 Hash apresentado'], ColunaTamanho = None, 
                            ColunasHash = [1, 2])
                NovoTamanho(self, 370, 590)
                

class janTransf(QDialog):
    def __init__(self,parent=None, LabelTexto = 'Copiando', ListaOutTabela = [], 
                 ListaArqIN = [], Destino = '', ListaArqOUT = [], ListaErro = [], 
                 DicControle = {'abortar':False, 'paraLista':False, 'substituir':False}, 
                 DicParaCHS={}):
        super(janTransf,self).__init__(parent)
        self.setWindowTitle(LabelTexto)
        self.setModal(True)
        self.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        
        lblTexto = QLabel(LabelTexto)
        lblProgresso = QLabel('0 de 0')
        pgbCopiando = QProgressBar()
        pgbCopiando.setValue(0)
        btAbortar = QPushButton(' Abortar ')
        btAbortar.setFocus(0)
        
        #___ LAYOUT ___
        layout = QVBoxLayout()
        layLbl = QHBoxLayout()
        layLbl.addWidget(lblTexto, 1, Qt.AlignRight)
        layLbl.addWidget(lblProgresso, 1, Qt.AlignLeft)
        layout.addLayout(layLbl)
        layout.addWidget(pgbCopiando)
        layout.addWidget(btAbortar, 1, Qt.AlignHCenter)
        
        self.setLayout(layout)
        
        #___ OBJETOS E VARIÁVEIS ___
        qProgresso = QObject()
        qTexto = QObject()
        qFim = QObject()
        self.qAbortar = QObject()
        self.boolTerminado = False
        
        
        #___ AÇÕES ___
        self.connect(qProgresso, SIGNAL('valueChanged(int)'), pgbCopiando.setValue)
        self.connect(qTexto, SIGNAL('valueChanged(int,int)'), lambda a, b: 
            lblProgresso.setText(str(a)+' de '+str(b)))
        self.connect(qFim, SIGNAL('fechar'), lambda: actFechar())
        self.connect(btAbortar, SIGNAL('clicked()'), lambda:actFechar(2))
        self.connect(self.qAbortar, SIGNAL('abortar'), lambda:actFechar(3))
        
        
        if DicControle['paraLista']:
            threadListaCHS = Thread(target = Transferencia, args=[ListaArqIN, ''], 
                                  kwargs={'qSigLabel':qTexto, 'qSigFim':qFim, 'qSigPorcent':qProgresso,
                                           'ListaErro':ListaErro, 'dicControle':DicControle, 
                                           'DicionParaCHS':DicParaCHS})
            threadListaCHS.start()

        else:
            threadTransf = Thread(target = Transferencia, args=[ListaArqIN, Destino], 
                                  kwargs={'qSigLabel':qTexto, 'qSigFim':qFim, 'qSigPorcent':qProgresso, 
                                          'ListaArquivosOUT':ListaArqOUT, 'ListaErro':ListaErro, 
                                          'dicControle':DicControle, 'ListaOutTabela':ListaOutTabela})
            threadTransf.start()
        
        #___ FUNÇÕES ___
        def actFechar(acao = 1):
            if acao == 1:
                self.boolTerminado = True
                btAbortar.setText('Fechar')
                self.close()
            elif acao == 2:
                DicControle['abortar']=True
                self.boolTerminado = True
                btAbortar.setText('Fechar')
                self.close()
            elif acao == 3:
                DicControle['abortar']=True

    def closeEvent(self, event):
        if not self.boolTerminado:
            questao = QMessageBox.question(self, 'Abortar', 'Deseja abortar o processo?',  'Sim', 
                                               'Não')
            if questao == 0:
                self.qAbortar.emit(SIGNAL('abortar'))
                self.boolTerminado = True
                event.accept()
            else:
                event.ignore()

#++++++++++ COPIAR =================
class JanCopiar(QDialog):
    def __init__(self,parent=None):
        super(JanCopiar,self).__init__(parent)
        self.setWindowTitle('ChecSplit - Copiar')
        self.setModal(True)
        self.setWindowFlags(Qt.Window)
        
        btCargArq = QPushButton(' Carregar arquivos ')
        chkTentar = QCheckBox()
        chkTentar.setText('Tentar novamente')
        chkTentar.setChecked(False)
        chkTentar.setToolTip('Serão efetuadas 3 tentativas de cópia')
        chkSubstituir = QCheckBox()
        chkSubstituir.setText('Substituir existentes')
        tableArqOrig = QTableWidget()
        tableArqOrig.setAlternatingRowColors(True)
        btDestino = QPushButton(' Destino ')
        inputDestino = QLineEdit()
        btCopiar = QPushButton(' Iniciar cópia ')
        groupCorrompidos = QGroupBox('Apresentaram erro ao copiar')
        tableCorromp = QTableWidget()
        tableCorromp.setAlternatingRowColors(True)
        btNovaTentativa=QPushButton(' Tentar novamente ')
        groupErroIO = QGroupBox('Erro na tentativa')
        tableErroIO = QTableWidget()
        tableErroIO.setAlternatingRowColors(True)
        

        #___ LAYOUT ___
        layDestino = QHBoxLayout()
        layDestino.addWidget(btDestino)
        layDestino.addWidget(inputDestino)
        
        layGroupErroIO = QVBoxLayout()
        layGroupErroIO.addWidget(tableErroIO)
        groupErroIO.setLayout(layGroupErroIO)
        
        layGroup = QVBoxLayout()
        layGroup.addWidget(tableCorromp)
        layGroup.addWidget(btNovaTentativa, 1, Qt.AlignHCenter)
        groupCorrompidos.setLayout(layGroup)
        
        layout = QVBoxLayout()
        layout.addWidget(btCargArq, 1, Qt.AlignHCenter)
        layout.addWidget(chkSubstituir, 1, Qt.AlignLeft)
        layout.addWidget(tableArqOrig)
        layout.addLayout(layDestino)
        layout.addWidget(btCopiar, 1, Qt.AlignHCenter)
        
        self.setLayout(layout)
        
        #___ VARIAVEIS ___
        listaArquivos = []
        listaArquivTable = []
        listaCopiados = []
        listaCopTable=[]
        listaErro = []
        listaDiferenca = []
        dicControle = {'abortar':False, 'paraLista':False, 'substituir':False}
        self.tituloCopia = 'Copiando'
        
        #___ AÇÕES ___
        self.connect(chkSubstituir, SIGNAL('stateChanged(int)'), lambda e: questSubst(e))
        self.connect(btCargArq, SIGNAL('clicked()'), lambda: selArquivos())
        self.connect(btCopiar, SIGNAL('clicked()'),
                     lambda: lancaTransf())
        self.connect(btDestino, SIGNAL('clicked()'), lambda: selDestino())
        self.connect(btNovaTentativa, SIGNAL('clicked()'), lambda: actTentNovamente())
        
        #___ FUNÇÕES ___
        def selArquivos():
            listarArquivos(ListaArquivos = listaArquivos, ListaParaTabela = listaArquivTable, 
                           numCampos=2, parent = self)

            PreenQTable(tableArqOrig,  listaArquivTable, HeaderLabels = ['Nome', 'Tamanho (KB)'], 
            BlockItens=[0, 1])
        
        def questSubst(estado):
            if estado != 0:
                texto = 'Esta opção fará com que os arquivos na pasta de destino que possuem o mesmo nome na origem sejam substituídos.\nDeseja isto realmente?'
                questao = QMessageBox.warning(self, 'Substituir', texto, 'Sim', 'Não')
                if questao == 0:
                    chkSubstituir.setCheckState(2)
                    dicControle['substituir'] = True
                else:
                    chkSubstituir.setCheckState(0)
                    dicControle['substituir'] = False
            else:
                dicControle['substituir'] = False
        
        def selDestino():
            destino = QFileDialog.getExistingDirectory(self, 'Selecionar destino.')
            inputDestino.setText(destino)
            
        def lancaTransf():
            destino = inputDestino.text()
            if listaArquivos==[]:
                questao = QMessageBox.question(self, 'Copiar', 
                'Deseja selecionar os arquivos a serem copiados?', 'Sim', 'Não')
                if questao == 0:
                    selArquivos()
                    lancaTransf()
            elif not os.path.isdir(destino):
                questao = QMessageBox.question(self, 'Copiar', 
                'Deseja selecionar o destino para as cópias?', 'Sim', 'Não')
                if questao == 0:
                    selDestino()
                    lancaTransf()
            else:
                listaErroAntes = listaErro[:]
                
                def LimparLista(Lista):
                    del Lista[0:len(Lista)]
                        
                LimparLista(listaCopiados)
                LimparLista(listaErro)
                LimparLista(listaCopTable)
                LimparLista(listaDiferenca)
                
                AbJanela(janTransf, parent = self, ListaArqIN=listaArquivos, Destino=destino, 
                         ListaArqOUT=listaCopiados, ListaErro=listaErro, ListaOutTabela=listaCopTable, 
                         DicControle = dicControle, LabelTexto = self.tituloCopia)
                
                listaCopTableEspelho = []
                for item in listaCopTable:
                    linha = [item[0], item[1], '']
                    listaCopTableEspelho.append(linha)
                
                AbJanela(JanHashCalc, listaCopiados, listaCopTableEspelho, parent=self, titulo = 'Checando')

                if listaErroAntes != listaErro:
                    layout.addWidget(groupErroIO)
                    PreenQTable(tableErroIO, listaErro, HeaderLabels = ['Nome', 'Tamanho (KB)', 'Motivo'], 
                    BlockItens=[0, 1, 2])
                    NovoTamanho(self, 500, 480)
                
                self.listaIndexes = []
                
                if actComparar(listaCopTable,  listaCopTableEspelho, listaDiferenca,
                               ColunaDeHash = 2,  ColunaNomes = 0, ListaIndices = self.listaIndexes):
                    layout.addWidget(groupCorrompidos)
                    PreenQTable(tableCorromp, listaDiferenca, 
                                HeaderLabels = ['Nome', 'Tamanho (KB)', 'Hash da cópia', 
                                                                            'Hash original'], ColunasHash = [2, 3], 
                                                                            BlockItens=[0, 1, 2, 3])
                    NovoTamanho(self, 500, 480)
                        
                elif listaErro == [] and listaDiferenca == []:
                    QMessageBox.information(self, 'Concluído', 'Cópia concluída com sucesso.')
                    btNovaTentativa.setDisabled(True)
                    tableCorromp.clear()
                    btCopiar.setDisabled(True)
                else:
                    msgerro = 'Cópia concluída, mas houveram falhas.'
                    QMessageBox.critical(self, 'Concluído', msgerro)
                
        def actTentNovamente():
            btCopiar.setDisabled(True)
            transfLista = listaArquivos[:]
            del listaArquivos[0:len(listaArquivos)]
            dicControle['substituir'] = True
            
            for indice in self.listaIndexes:	
                listaArquivos.append(transfLista[indice])

            lancaTransf()
#++++++++++++++++++++++++++++++ CORPO +++++++++++++++++++++++++++++++++++++++++
if __name__ == '__main__':
    app=QApplication([])
    form=ChecSplit()
    form.show()
    app.exec_()





